#ifndef KPEXTMEMORYRESOURCE_H
#define KPEXTMEMORYRESOURCE_H

#include "kpextCommonDef.h"
#include "kpextCommandBuffer.h"
#include "kpextDescriptorMgr.h"

namespace kpext {

/**
 * @brief vk::Format格式与其名称映射表
 */
extern std::map<
vk::Format const, std::string const> const VKFormatEnumMap;

/**
 * @brief 纹素缓冲对象感兴趣格式
 */
extern std::vector<vk::Format> const TexelBufInterestFormats;

/**
 * @brief 将vulkan vk::Format 格式转化为字符中
 * @param fomat  vulkan 纹素格式
 * @return 成功返回格式的字符名，否则返回格式的无符号整数值字符串
 */
std::string vkFormat2Str(const vk::Format  & format);

/**
 * @brief 检查格式属性的是否支持纹理缓冲
 * @param fprops  输入格式属性
 * @param texelbuf 如果支持纹素缓冲储存返回true， 否则返回false
 * @param uniformtexelbuf  如果支持uniform纹素缓冲返回true， 否则返回false
 * @param texelbufatomic  如果支持纹素缓冲储存原子操作返回true， 否则返回false
 */
void checkTexelBufFormat(vk::FormatProperties & fprops,
                         bool * texelbuf,
                         bool * uniformtexelbuf,
                         bool * texelbufatomic);


//---------------- class declare  --------------------
class RawBuffer;
class RawDeviceMemory;
class BaseBuffer;
struct CmdBaseBufferCpy;
struct CmdBaseBufferSet;
struct CmdMemoryBarrier;
struct CmdBuffersMemBarrier;
class StagingBuffer;
//----------------  --------------------


/**
 * @brief vk::Buffer的简单包装类， 它需要绑定到vk::DeviceMemory才能拥有实际的内存
 */
class RawBuffer : public IGetDecriptorInfo
{
public:
    /**
     * @brief 构造函数， 默认不会创建vulkan buffer 对象
     * @param phydevice  vulkan物理设备指针
     * @param device  vulkan逻辑设备指针
     */
    explicit RawBuffer(std::shared_ptr<vk::PhysicalDevice> phydevice,
                    std::shared_ptr<vk::Device> device);
    virtual ~RawBuffer() override;

    /**
     * @brief 重新创建vulkan buffer对象
     * @param bytesSize vulkan buffer所需的字节尺寸
     * @param usage 用途
     * @param sharing 共享模式，如果为~0表示使用上一次的值， eExclusive表示队列家族独占（第一个使用buffer的队列家族拥有它）
     * @return 成功返回true, 否则返回false
     */
    virtual bool recreate(
            size_t bytesSize,
            vk::BufferUsageFlags usage,
            vk::SharingMode sharing = static_cast<vk::SharingMode>(~0)
            );

    /**
     * @brief 获取vulkan设备缓冲对象指针， 获取到的指针不应在外部调用destory()销毁
     * @return vulkan设备缓冲对象指针
     */
    virtual vk::Buffer * getVKBuf();

    /**
     * @brief 获取显存要求， 用于创建vulkan DeviceMemory对象
     * @return  vulkan DeviceMemory对象
     */
    virtual vk::MemoryRequirements getMemRequirements();

    /**
     * @brief 将vulkan DeviceMemory对象绑定到 本vulkan Buffer对象
     * @param memory vulkan DeviceMemory对象
     * @param offset  从vulkan DeviceMemory 显存偏移字节开始进行绑定
     * @return 成功返回true，否则返回false
     */
    virtual bool bindMemory(kpext::RawDeviceMemory & memory, size_t offset = 0);

    /**
     * @brief 重新创建buffer view对象
     * @param format  纹素格式
     * @param offset  起始字节位置
     * @param end   结束字节位置
     * @return 成功返回true,失败返回false
     */
    virtual bool recreateBufView(vk::Format format,
                                 size_t offset = 0,
                                 size_t end = 0);


    static bool recordCopy(const vk::CommandBuffer& commandBuffer,
                           const RawBuffer & dstBuf,
                           const RawBuffer & srcBuf,
                           size_t size = std::numeric_limits<size_t>::max(),
                           size_t dstOffset = 0,
                           size_t srcOffset = 0
            );

    /**
     * @brief 根据本vulkan buffer对象， 获取对应的vk::Buffer描述符信息
     * @param offset 指定起始偏移字节
     * @param end  指定结束字节偏移
     * @return vk::DescriptorBufferInfo对象
     */
    virtual vk::DescriptorBufferInfo getDescBufInfo(size_t offset = 0,
                                                    size_t end = 0);

    //IGetDecriptorInfo interface
    vk::DescriptorSetLayoutBinding getDescriptorSetLayoutBinding() override;

    virtual std::shared_ptr<vk::DescriptorBufferInfo>
    getDescriptorBufferInfo() override;

    std::shared_ptr<vk::BufferView> getBufferView() override;

protected:

    /**
      * @brief vulkan 物理设备
     */
    std::shared_ptr<vk::PhysicalDevice> m_phyDevice;

    /**
     * @brief vulkan  逻辑设备
     */
    std::shared_ptr<vk::Device> m_device;

    /**
     * @brief 期望的缓冲区字节数
     */
    size_t m_expectBytesSize;

    /**
     * @brief 用途
     */
    vk::BufferUsageFlags m_usage;

    /**
     * @brief 不同的队列家族间的共享模式
     */
    vk::SharingMode m_sharing;

    /**
     * @brief vk::Buffer共享指针
     */
    std::shared_ptr<vk::Buffer> m_buf;

    /**
     * @brief vk::BufferView共享指针, 用于texel buffer
     */
    std::shared_ptr<vk::BufferView> m_bufview;

    /**
     * @brief 用于texel buffer的纹素格式
     */
    vk::Format m_format;

}; // class Buffer

// --------------------------------------------------------

class RawDeviceMemory{
public:

    /**
     * @brief 默认创建的内存特性
     */
    static const vk::MemoryPropertyFlags DefMemProp;

    /**
     * @brief 构造函数， 默认不会创建vulkan DeviceMemory 对象
     * @param device  vulkan逻辑设备指针
     * @param memprops  vulkan物理设备支持的特性
     */
    explicit RawDeviceMemory(std::shared_ptr<vk::Device> device,
                          vk::PhysicalDeviceMemoryProperties const & memprops
                          );
    virtual ~RawDeviceMemory();


    /**
     * @brief 重新创建vulkan DeviceMemory对象
     * @param memoryRequirements 要求创建的显存信息
     * @param prop  要求创建的显存信息属性，默认创建cpu可见的一致显存
     * @return 成功返回true，否则返回false
     */
    virtual bool recreate(vk::MemoryRequirements const & memoryRequirements,
                          vk::MemoryPropertyFlags prop = DefMemProp
            );

    /**
     * @brief 获取vulkan设备显存对象指针， 获取到的指针不应在外部调用destory()销毁
     * @return vulkan设备显存对象指针
     */
    virtual vk::DeviceMemory * getVKDevMem();

    /**
     * @brief 映射vulkan设备显存到主机内存， 并返回主机端可访问内存地址
     * @return 成功返回主机端可访问内存地址，否则返回空地址
     */
    virtual void * mapToHostMemory(size_t offset = 0,
                                   size_t size = 0
                                   );

    /**
     * @brief 取消映射vulkan设备显存到主机内存
     */
    virtual void unmapFromHostMemory();

    /**
     * @brief 将主机端写入的数据同步到vulkan设备端，如果同步大小不是全缓冲，那么字节大小必须是nonCoherentAtomSize的整数倍
     * @param size   同步的数据大小，单位字节
     * @param offset  同步的偏移地址，单位字节
     */
    virtual void flushMapped(size_t size = ~0ULL, size_t offset = 0);

    /**
     * @brief 将vulkan设备端写入的数据同步到主机端，如果同步大小不是全缓冲，那么字节大小必须是nonCoherentAtomSize的整数倍
     * @param size   同步的数据大小，单位字节
     * @param offset  同步的偏移地址，单位字节
     */
    virtual void invalidateMapped(size_t size = ~0ULL, size_t offset = 0);

protected:

    /**
     * @brief vulkan 物理设备支持的特性
     */
    const vk::PhysicalDeviceMemoryProperties m_supportMemProps;

    /**
     * @brief vulkan 逻辑设备
     */
    std::shared_ptr<vk::Device> m_device;

    /**
     * @brief 创建的vulkan设备显存要求
     */
    vk::MemoryRequirements m_memReq;

    /**
     * @brief 创建的vulkan设备显存特性
     */
    vk::MemoryPropertyFlags m_memProp;

    /**
     * @brief 创建的vulkan设备显存对象指针
     */
    std::shared_ptr<vk::DeviceMemory> m_memory;

    /**
     * @brief 标记是否已经映射到主机内存地址
     */
    bool m_isMapped;

}; // class DeviceMemory

// --------------------------------------------------------

/**
 * @brief kpext一些高级别缓冲类的基类
 */
class BaseBuffer{
public:
    /**
     * @brief 构造函数
     * @param buf  vk::Buffer的简单包装对象共享指针
     * @param mem  vk::DeviceMemory的简单包装对象共享指针
     * @param size 期望缓冲拥有的显存字节数
     * @param usage 用途
     * @param  memprop  期望显存属性
     */
    explicit BaseBuffer(std::shared_ptr<kpext::RawBuffer> buf,
                        std::shared_ptr<kpext::RawDeviceMemory> mem,
                        size_t size,
                        vk::BufferUsageFlags usage,
                        vk::MemoryPropertyFlags memprop
                        ):
        m_bufobj(buf),
        m_memory(mem),
        m_size(size),
        m_usage(usage),
        m_memProp(memprop)
    {
    }

    virtual ~BaseBuffer(){
        m_bufobj = nullptr;
        m_memory = nullptr;
    }

    /**
     * @brief 重新构建整个缓冲区
     * @param buf  vk::Buffer的简单包装对象共享指针
     * @param mem  vk::DeviceMemory的简单包装对象共享指针
     * @param size 希望重构后的缓冲拥有的显存大小，单位为字节
     */
    virtual void rebuild(std::shared_ptr<kpext::RawBuffer> buf,
                         std::shared_ptr<kpext::RawDeviceMemory> mem,
                         size_t size
                         );

    /**
     * @brief 重新构建整个缓冲区 重载函数
     * @param buf  vk::Buffer的简单包装对象共享指针
     * @param mem  vk::DeviceMemory的简单包装对象共享指针
     * @param size 希望重构后的缓冲拥有的显存大小，单位为字节
     */
    virtual void rebuild(std::shared_ptr<kpext::RawBuffer> buf,
                         std::shared_ptr<kpext::RawDeviceMemory> mem,
                         size_t size,
                         vk::BufferUsageFlags usage,
                         vk::MemoryPropertyFlags memprop
                         );


    /**
     * @brief 更改缓冲对象显存大小
     * @param size  希望更改后的缓冲拥有的显存大小，单位为字节
     */
    virtual void resize(size_t size);

    /**
     * @brief 获取当前缓冲区拥有的显存大小，单位为字节
     * @return  缓冲区拥有的显存大小
     */
    virtual size_t getSize() const {return m_size;}

//    /**
//     * @brief 获取vulkan buffer的祼包装类对象共享指针
//     * @return  vulkan buffer的祼包装类对象共享指针
//     */
//    virtual std::shared_ptr<kpext::RawBuffer> rawBuf(){
//        return m_bufobj;
//    }

//    /**
//     * @brief 获取vulkan devicememory的祼包装类对象共享指针
//     * @return vulkan devicememory的祼包装类对象共享指针
//     */
//    virtual std::shared_ptr<kpext::RawDeviceMemory> rawMem(){
//        return m_memory;
//    }

    /**
         * @brief 取得描述符信息获取器
         * @return 成功返回描述符信息获取器指针，失败返回空指针
         */
    virtual IGetDecriptorInfo * descrInfoGetter(){
        return m_bufobj.get();
    }

    /**
     * @brief 记录一条kpext::BaseBuffer基本复制指令到命令缓冲区
     * @param commandBuffer 记录到的目标vulkan 命令缓冲区
     * @param dstBuf 复制的目标缓冲
     * @param srcBuf 复制的源缓冲
     * @param size 要复制的字节数, ~0ULL表示按照目标缓冲区大小全部复制
     * @param dstOffset 目标缓冲起始字节偏移
     * @param srcOffset 源缓冲起始字节偏移
     * @return 成功返回true, 否则返回false
     */
    static bool recordCopy(const vk::CommandBuffer& commandBuffer,
                           const BaseBuffer & dstBuf,
                           const BaseBuffer & srcBuf,
                           size_t size = ~0ULL,
                           size_t dstOffset = 0,
                           size_t srcOffset = 0
            ){
        if(!dstBuf.m_bufobj || !srcBuf.m_bufobj){
            SPDLOG_ERROR(__FUNCTION__ " input invalid kpext::BaseBuffer");
            return false;
        }

        if(size == ~0ULL){
            size = dstBuf.getSize();
        }

        vk::BufferCopy copyRegion(srcOffset, dstOffset, size);
        commandBuffer.copyBuffer(*(srcBuf.m_bufobj->getVKBuf()),
                                 *(dstBuf.m_bufobj->getVKBuf()),
                                 copyRegion
                                 );
        return true;
    }

    /**
     * @brief 记录一条kpext::BaseBuffer基本设定值指令到命令缓冲区
     * @param commandBuffer 记录到的目标vulkan 命令缓冲区
     * @param dstBuf 设定值的目标缓冲
     * @param val 设定的值，注意它是4字节重复写入到缓冲区的，写入字节序端模式与cpu相同
     * @param size  要设定的字节数，必须是4的整数倍
     * @param offset 从缓冲区偏移字节数据开始设定值，必须是4的整数倍
     * @return 成功返回true, 否则返回false
     */
    static bool recordSet(const vk::CommandBuffer& commandBuffer,
                          const BaseBuffer & dstBuf,
                          const uint32_t val = 0,
                          size_t size = ~0ULL,
                          size_t offset = 0
            ){
        if(!dstBuf.m_bufobj){
            SPDLOG_ERROR(__FUNCTION__ " input invalid kpext::BaseBuffer");
            return false;
        }

        if(size == ~0ULL){
            size = VK_WHOLE_SIZE;
        }

        commandBuffer.fillBuffer(*(dstBuf.m_bufobj->getVKBuf()),
                                 offset,
                                 size,
                                 val
                                 );

        return true;
    }

//    typedef struct VkBufferMemoryBarrier {
//        VkStructureType    sType;
//        const void*        pNext;
//        VkAccessFlags      srcAccessMask;
//        VkAccessFlags      dstAccessMask;
//        uint32_t           srcQueueFamilyIndex;
//        uint32_t           dstQueueFamilyIndex;
//        VkBuffer           buffer;
//        VkDeviceSize       offset;
//        VkDeviceSize       size;
//    } VkBufferMemoryBarrier;

    /**
     * @brief 生成一个缓冲内存对象访问屏障
     * @param result 生成的结果
     * @param buf 进行屏障访问的缓冲内存对象
     * @param srcAccess  源访问掩码
     * @param dstAccess  目标访问掩码
     * @param srcQueue  源队列，用于不同队列共享的缓冲内存对象的队列权属转移
     * @param dstQueue  目标队列
     * @param offset  屏障访问的显存字节偏移
     * @param size  屏障访问的显存字节数量
     */
    static void getBufferMemBarrier(vk::BufferMemoryBarrier & result,
                                    std::shared_ptr<BaseBuffer> buf,
                                    vk::AccessFlags srcAccess,
                                    vk::AccessFlags dstAccess,
                                    uint32_t srcQueue = VK_QUEUE_FAMILY_IGNORED,
                                    uint32_t dstQueue = VK_QUEUE_FAMILY_IGNORED,
                                    size_t offset = 0,
                                    size_t size = ~0ULL
            ){
        if(!buf){
            SPDLOG_ERROR(__FUNCTION__ " input null pointer!");
            return;
        }
        result.setSrcAccessMask(srcAccess);
        result.setDstAccessMask(dstAccess);
        result.setSrcQueueFamilyIndex(srcQueue);
        result.setDstQueueFamilyIndex(dstQueue);
        result.setBuffer(*buf->m_bufobj->getVKBuf());
        result.setOffset(offset);
        if(size == ~0ULL || size > buf->getSize()){
            size = buf->getSize();
        }
        result.setSize(size);
    }

protected:

    /**
     * @brief vk::Buffer的简单包装对象共享指针
     */
    std::shared_ptr<kpext::RawBuffer> m_bufobj;

    /**
     * @brief vk::DeviceMemory的简单包装对象共享指针
     */
    std::shared_ptr<kpext::RawDeviceMemory> m_memory;

    /**
     * @brief 缓冲区占用的字节
     */
    size_t m_size;

    /**
     * @brief 缓冲区的用途
     */
    vk::BufferUsageFlags m_usage;

    /**
     * @brief 显存属性
     */
    vk::MemoryPropertyFlags m_memProp;

}; // class BaseBuffer

// ---------------------------------------------------

/**
 * @brief 基本缓冲复制的便利结构体
 */
struct CmdBaseBufferCpy : public IRecordCommands
{
public:
    explicit CmdBaseBufferCpy(){}
    ~CmdBaseBufferCpy() override{}

    /**
     * @brief 将复制的源和目标信息设置准备好
     * @param dstBuf 复制的目标缓冲
     * @param srcBuf 复制的源缓冲
     * @param size 要复制的字节数, ~0ULL表示按照目标缓冲区大小全部复制
     * @param dstOffset 目标缓冲起始字节偏移
     * @param srcOffset 源缓冲起始字节偏移
     */
    inline void prepare(std::shared_ptr<BaseBuffer> dstBuf,
                        std::shared_ptr<BaseBuffer> srcBuf,
                        size_t size = ~0ULL,
                        size_t dstOffset = 0,
                        size_t srcOffset = 0){
        this->dstBuf = dstBuf;
        this->srcBuf = srcBuf;
        this->size = size;
        this->dstOffset = dstOffset;
        this->srcOffset = srcOffset;
    }

    // IRecordCommands interface
    void record(const vk::CommandBuffer &commandBuffer) override{
        if(!dstBuf || !srcBuf){
            SPDLOG_ERROR(__FUNCTION__ " input invalid kpext::BaseBuffer");
            return;
        }
        if(!BaseBuffer::recordCopy(
                    commandBuffer,
                    *dstBuf,
                    *srcBuf,
                    size,
                    dstOffset,
                    srcOffset
                    )){
            SPDLOG_ERROR(__FUNCTION__ " record kpext::BaseBuffer copy failed!");
        }
    }

public:
    // 复制的目标缓冲
    std::shared_ptr<BaseBuffer> dstBuf = nullptr;
    // 复制的源缓冲
    std::shared_ptr<BaseBuffer> srcBuf = nullptr;
    // 要复制的字节数, ~0ULL表示按照目标缓冲区大小全部复制
    size_t size = ~0ULL;
    //  目标缓冲起始字节偏移
    size_t dstOffset = 0;
    //  源缓冲起始字节偏移
    size_t srcOffset = 0;
}; // struct kpext::CmdBaseBufferCpy

// -----------------------------------------------

/**
 * @brief 基本缓冲赋相同值的便利结构体
 */
struct CmdBaseBufferSet : public IRecordCommands
{
public:
    explicit CmdBaseBufferSet(){}
    ~CmdBaseBufferSet() override{}

    /**
     * @brief 将要进行赋值填充的目标缓冲信息设置准备好
     * @param dstBuf 设定值的目标缓冲
     * @param val 设定的值，注意它是4字节重复写入到缓冲区的，写入字节序端模式与cpu相同
     * @param size  要设定的字节数，必须是4的整数倍
     * @param offset 从缓冲区偏移字节数据开始设定值，必须是4的整数倍
     */
    inline void prepare(std::shared_ptr<BaseBuffer> dstBuf,
                        const uint32_t val = 0,
                        size_t size = ~0ULL,
                        size_t offset = 0){
        this->dstBuf = dstBuf;
        this->val = val;
        this->size = size;
        this->offset = offset;
    }

    // IRecordCommands interface
    void record(const vk::CommandBuffer &commandBuffer) override {
        if(!dstBuf){
            SPDLOG_ERROR(__FUNCTION__ " input invalid kpext::BaseBuffer");
            return;
        }
        if(!BaseBuffer::recordSet(commandBuffer,
                                  *dstBuf,
                                  val,
                                  size,
                                  offset
                                  )){
            SPDLOG_ERROR(__FUNCTION__ " record kpext::BaseBuffer fill failed!");
        }
    }

public:
    //  赋值的目标缓冲
    std::shared_ptr<BaseBuffer> dstBuf = nullptr;
    //  要赋值的字节数, ~0ULL表示按照目标缓冲区大小全部赋值，必须为4的整数倍(连续4字节uint32赋值)
    size_t size = ~0ULL;
    //  要赋值的目标缓冲起始字节偏移， 必须为4的整数倍
    size_t offset = 0;
    //  赋值
    uint32_t val = 0;
}; // struct kpext::CmdBaseBufferSet;

// -----------------------------------------------

/**
 * @brief 基本vulkan管线内存屏障便利结构体
 */
struct CmdMemoryBarrier : public IRecordCommands
{
public:
    explicit CmdMemoryBarrier(){}
    ~CmdMemoryBarrier() override {}

    /**
     * @brief 准备如要记录的内存屏障信息
     * @param srcStage  在屏障前哪个管线阶段等待访问完成
     * @param dstStage  保证哪个管线阶段在屏障后才执行，访问是同步一致的
     * @param srcAccess  在屏障前等待管线阶段完成的什么访问
     * @param dstAccess  在屏障后保证管线阶段的什么访问是同步一致的
     */
    inline void prepare(vk::PipelineStageFlagBits srcStage,
                        vk::PipelineStageFlagBits dstStage,
                        vk::AccessFlags srcAccess,
                        vk::AccessFlags dstAccess){
        this->srcStage = srcStage;
        this->dstStage = dstStage;
        memBarrier = vk::MemoryBarrier(srcAccess,
                                       dstAccess);
    }

    // IRecordCommands interface
    void record(const vk::CommandBuffer &commandBuffer) override{
        commandBuffer.pipelineBarrier(
                    srcStage,
                    dstStage,
                    vk::DependencyFlags(),
                    1, &this->memBarrier,
                    0, nullptr,
                    0, nullptr
                    );
    }

public:
    vk::PipelineStageFlagBits srcStage = vk::PipelineStageFlagBits();
    vk::PipelineStageFlagBits dstStage = vk::PipelineStageFlagBits();
    vk::MemoryBarrier memBarrier = vk::MemoryBarrier();
}; // struct kpext::CmdMemoryBarrier

// -----------------------------------------------

/**
 * @brief 基本vulkan管线缓冲内存对象屏障便利结构体
 */
struct CmdBuffersMemBarrier : public IRecordCommands
{
public:
    explicit CmdBuffersMemBarrier(){}
    ~CmdBuffersMemBarrier() override {}

    /**
     * @brief 准备对一个缓冲内存对象进行屏障
     * @param buf  进行屏障访问的缓冲内存对象
     * @param srcStage  在屏障前哪个管线阶段等待访问完成
     * @param dstStage  保证哪个管线阶段在屏障后才执行，访问是同步一致的
     * @param srcAccess  源访问掩码
     * @param dstAccess  目标访问掩码
     * @param offset  屏障访问的显存字节偏移
     * @param size  屏障访问的显存字节数量
     * @param srcQueue  源队列，用于不同队列共享的缓冲内存对象的队列权属转移
     * @param dstQueue  目标队列
     */
    inline void prepare(const std::shared_ptr<BaseBuffer> & buf,
                        vk::PipelineStageFlagBits srcStage,
                        vk::PipelineStageFlagBits dstStage,
                        vk::AccessFlags srcAccess,
                        vk::AccessFlags dstAccess,
                        size_t offset = 0,
                        size_t size = ~0ULL,
                        uint32_t srcQueue = VK_QUEUE_FAMILY_IGNORED,
                        uint32_t dstQueue = VK_QUEUE_FAMILY_IGNORED
            ){
        barriers.clear();
        barriers.push_back(vk::BufferMemoryBarrier());
        this->srcStage = srcStage;
        this->dstStage = dstStage;
        BaseBuffer::getBufferMemBarrier(
                    barriers.front(),
                    buf,
                    srcAccess,
                    dstAccess,
                    srcQueue,
                    dstQueue,
                    offset,
                    size
                    );
    }

    /**
     * @brief 准备对多个缓冲内存对象进行屏障
     * @param bufs  进行屏障访问的多个缓冲内存对象
     * @param srcStage  在屏障前哪个管线阶段等待访问完成
     * @param dstStage  保证哪个管线阶段在屏障后才执行，访问是同步一致的
     * @param srcAccess  源访问掩码
     * @param dstAccess  目标访问掩码
     * @param srcQueue  源队列，用于不同队列共享的缓冲内存对象的队列权属转移
     * @param dstQueue  目标队列
     */
    void prepare(const std::vector<std::shared_ptr<BaseBuffer>> & bufs,
                 vk::PipelineStageFlagBits srcStage,
                 vk::PipelineStageFlagBits dstStage,
                 vk::AccessFlags srcAccess,
                 vk::AccessFlags dstAccess,
                 uint32_t srcQueue = VK_QUEUE_FAMILY_IGNORED,
                 uint32_t dstQueue = VK_QUEUE_FAMILY_IGNORED
            ){
        barriers.clear();
        auto count = bufs.size();
        bool ok = count > 0;
        for(const auto & b : bufs){
            if(!b){
                ok = false;
                break;
            }
        }
        if(!ok){
            SPDLOG_ERROR(__FUNCTION__ " input invalid kpext::BaseBuffer vector");
            return;
        }
        this->srcStage = srcStage;
        this->dstStage = dstStage;
        barriers.resize(count);
        size_t i = 0;
        for (auto & b : bufs) {
            BaseBuffer::getBufferMemBarrier(
                        barriers.data()[i],
                        b,
                        srcAccess,
                        dstAccess,
                        srcQueue,
                        dstQueue
                        );
            i++;
        }
    }

    // IRecordCommands interface
    void record(const vk::CommandBuffer &commandBuffer) override{
        if(barriers.size() < 1){
            SPDLOG_WARN("BufferMemoryBarries vector size less than 1!");
            return;
        }

        commandBuffer.pipelineBarrier(
                    srcStage,
                    dstStage,
                    vk::DependencyFlags(),
                    0, nullptr,
                    static_cast<uint32_t>(barriers.size()),
                    barriers.data(),
                    0, nullptr
                    );
    }

public:
    std::vector<vk::BufferMemoryBarrier> barriers = {};
    vk::PipelineStageFlagBits srcStage = vk::PipelineStageFlagBits();
    vk::PipelineStageFlagBits dstStage = vk::PipelineStageFlagBits();
};  // struct kpext::CmdBuffersMemBarrier;

// -----------------------------------------------

/**
 * @brief 暂存缓冲对象，一般用于将平台主机内存与vulkan设备显存之前传输交换数据
 */
class StagingBuffer : public BaseBuffer{
public:
    static constexpr vk::BufferUsageFlags BufUsage =
            vk::BufferUsageFlagBits::eTransferSrc |
            vk::BufferUsageFlagBits::eTransferDst // | vk::BufferUsageFlagBits::eStorageBuffer |
            ;

    static constexpr std::array<vk::MemoryPropertyFlags, 4> BufMemProp ={
        vk::MemoryPropertyFlagBits::eHostVisible,
        vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent,
        vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCached,
        vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent | vk::MemoryPropertyFlagBits::eHostCached
    };

    explicit StagingBuffer(std::shared_ptr<kpext::RawBuffer> buf,
                           std::shared_ptr<kpext::RawDeviceMemory> mem,
                           size_t size,
                           bool coherent = true,
                           bool cached = false
                           );

    ~StagingBuffer() override;

    /**
     * @brief 将当前缓冲区拥有的显存映射到主机内存，并返回内存地址
     * @return  成功返回主机内存访问暂存缓冲区的地址，否则返回空指针
     */
    void * map();

    /**
     * @brief 将当前缓冲区拥有的显存映射到主机内存，并返回内存地址
     * @return  成功返回主机内存访问暂存缓冲区的地址，否则返回空指针
     */
    template<typename T>
    T * data(){
        void * ptr = map();
        return reinterpret_cast<T *>(ptr);
    }

    /**
     * @brief 以模板类型vector返回内部数据幅本，一般只用于辅助打印之类
     * @return
     */
    template<typename T>
    std::vector<T> vector(){
        std::vector<T> ret;
        T * ptr = reinterpret_cast<T *>(map());
        auto size = m_size / sizeof (T);
        ret.resize(size);
        if(ptr && size > 0){
            ret.assign(ptr,
                       ptr + size
                       );
        }
        unmap();
        return ret;
    }

    /**
     * @brief 取消当前缓冲区拥有的显存映射到主机内存
     */
    void unmap();

    /**
     * @brief 将主机端写入的数据同步到vulkan设备端，如果同步大小不是全缓冲，那么字节大小必须是nonCoherentAtomSize的整数倍
     * @param size   同步的数据大小，单位字节
     * @param offset  同步的偏移地址，单位字节
     */
    virtual void flushMapped(size_t size = ~0ULL, size_t offset = 0);

    /**
     * @brief 将vulkan设备端写入的数据同步到主机端，如果同步大小不是全缓冲，那么字节大小必须是nonCoherentAtomSize的整数倍
     * @param size   同步的数据大小，单位字节
     * @param offset  同步的偏移地址，单位字节
     */
    virtual void invalidateMapped(size_t size = ~0ULL, size_t offset = 0);

}; //class kpext::StagingBuffer;

//-------------------------------------------------------

class DeviceBuffer : public BaseBuffer
{
public:
    static constexpr vk::BufferUsageFlags BufUsage =
            vk::BufferUsageFlagBits::eStorageBuffer|
            vk::BufferUsageFlagBits::eTransferSrc |
            vk::BufferUsageFlagBits::eTransferDst
            ;

    static constexpr vk::MemoryPropertyFlags BufMemProp =
            vk::MemoryPropertyFlagBits::eDeviceLocal;

    explicit DeviceBuffer(std::shared_ptr<kpext::RawBuffer> buf,
                           std::shared_ptr<kpext::RawDeviceMemory> mem,
                           size_t size
                           )
        :BaseBuffer(buf, mem, size, BufUsage, BufMemProp)
    {
        this->m_size = size;
        if(m_bufobj && m_memory){
            rebuild(m_bufobj, m_memory, size);
        }
    }

    ~DeviceBuffer() override {}
}; //class kpext::DeviceBuffer

//-------------------------------------------------------

class UniformBuffer : public BaseBuffer
{
public:
    static constexpr vk::BufferUsageFlags BufUsage =
            vk::BufferUsageFlagBits::eUniformBuffer |
            vk::BufferUsageFlagBits::eTransferSrc |
            vk::BufferUsageFlagBits::eTransferDst
            ;

    static constexpr vk::MemoryPropertyFlags BufMemProp =
            vk::MemoryPropertyFlagBits::eDeviceLocal;

    explicit UniformBuffer(std::shared_ptr<kpext::RawBuffer> buf,
                           std::shared_ptr<kpext::RawDeviceMemory> mem,
                           size_t size
                           )
        :BaseBuffer(buf, mem, size, BufUsage, BufMemProp)
    {
        this->m_size = size;
        if(m_bufobj && m_memory){
            rebuild(m_bufobj, m_memory, size);
        }
    }

    ~UniformBuffer() override {}

}; // class kpext::UniformBuffer

//-------------------------------------------------------


class UniformTexelBuffer : public BaseBuffer
{
public:
    static constexpr vk::BufferUsageFlags BufUsage =
            vk::BufferUsageFlagBits::eUniformTexelBuffer |
            vk::BufferUsageFlagBits::eTransferSrc |
            vk::BufferUsageFlagBits::eTransferDst
            ;

    static constexpr vk::MemoryPropertyFlags BufMemProp =
            vk::MemoryPropertyFlagBits::eDeviceLocal;

    explicit UniformTexelBuffer(std::shared_ptr<kpext::RawBuffer> buf,
                           std::shared_ptr<kpext::RawDeviceMemory> mem,
                           size_t size,
                           vk::Format format = vk::Format::eR32Sfloat
                           )
        :BaseBuffer(buf, mem, size, BufUsage, BufMemProp)
    {
        this->m_size = size;
        if(m_bufobj && m_memory){
            rebuild(m_bufobj, m_memory, size);
            if(!buf->recreateBufView(format)){
                SPDLOG_ERROR("create UniformTexelBuffer view failed!");
            }
        }
    }

    ~UniformTexelBuffer() override {}

}; // class kpext::UniformBuffer

//-------------------------------------------------------

} // namespace kpext

#endif // KPEXTMEMORYRESOURCE_H
